/*
 * This file is part of Euphrat.
 *
 * Copyright © 2012 Bart Verhoeven
 *
 * Euphrat is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Euphrat is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Euphrat. If not, see <http://www.gnu.org/licenses/>.
 */
package org.euphrat.client.view.tab;

import static com.google.gwt.user.client.DOM.setStyleAttribute;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Widget;

import java.util.List;
import org.euphrat.client.view.widget.HoverLink;

/**
 * <p>A horizontal, styled menu widget holding zero or more {@link Tab Tabs}.
 * The tab {@link Tab#getName() name} is displayed as a hyperlink with a hover
 * effect. The {@link Tab#asWidget() widget} itself, however, is not displayed.
 *
 * <p>An active tab takes its native {@link Tab#getBackgroundColor() color} when
 * selected. All inactive tab share the same color. In addition, when {@link
 * HoverLink hovering} the mouse over an inactive tab, the tab gets its native
 * color as if it were selected.</p>
 *
 * <p>The following CSS styles are applied:
 * <ul>
 *   <li>{@code .tabMenu} (tab menu itself)</li>
 *   <li>{@code .tabMenu .tabItem} (unselected tab)</li>
 *   <li>{@code .tabMenu .tabItem-selected} (selected tab)</li>
 *   <li>{@code .tabMenu .firstTabItem} (first tab)</li>
 *   <li>{@code .tabMenu .firstTabItem-selected} (first tab if selected)</li>
 * </ul></p>
 *
 * @author Bart Verhoeven
 */
public class TabMenu extends Composite {

  /** Main style applied on this tab meu. */
  private static final String TAB_MENU_STYLE = "tabMenu";
  /** Style of the first tab in this menu. */
  private static final String FIRST_TAB_STYLE = "firstTabItem";
  /** Style of all but the first tab in this menu. */
  private static final String TAB_STYLE = "tabItem";
  /** Suffix appended to all tab styles if the tab is selected. */
  private static final String SELECTED_TAB_STYLE_SUFFIX = "-selected";
  /** Name of the background color style attribute. */
  private static final String BACKGROUND_COLOR = "backgroundColor";

  /** Horizontal panel where all hyperlinks are placed upon. */
  private final HorizontalPanel panel = new HorizontalPanel();
  /** All tab that were added to this menu. */
  private final List<Tab> tabs = Lists.newArrayList();
  /** Index of the currently selected tab. */
  private int selectionIndex = -1;

  /**
   * Creates a new tab menu.
   */
  public TabMenu() {
    initWidget(panel);
    setStyleName(TAB_MENU_STYLE);
  }

  /**
   * Returns the tab at the given index, or {@code null} if no such tab exists.
   *
   * @param index index of the tab to retrieve
   * @return the tab at the given index, or {@code null} if no such tab exists
   */
  public Tab getTab(int index) {
    if (index < 0 || index >= tabs.size()) {
      return null;
    }

    return tabs.get(index);
  }

  /**
   * Returns all tab in this menu.
   *
   * @return an unmodifiable list of all tab in this menu
   */
  public List<Tab> getTabs() {
    return ImmutableList.copyOf(tabs);
  }

  /**
   * Returns the selected tab, or {@code null} if no tab is selected.
   *
   * @return the selected tab, or {@code null} if no tab is selected
   */
  public Tab getSelectedTab() {
    return getTab(selectionIndex);
  }

  /**
   * Adds a tab to this menu.
   *
   * @param tab the tab to add
   */
  public void addTab(Tab tab) {
    if (tab == null) {
      return;
    }

    // The link highlights the tab when hovering the mouse over the tab.
    HoverLink link = new HoverLink(tab.getName(), tab.getBackgroundColor());
    panel.add(link);
    tabs.add(tab);

    // Apply a style to the tab widget.
    styleWidget(link, false);
  }

  /**
   * Removes a tab from this menu, if this menu contains it.
   *
   * @param tab the tab to remove
   */
  public void removeTab(Tab tab) {
    if (tab == null) {
      return;
    }

    // If tab is present in this tab menu, remove it.
    int index = tabs.indexOf(tab);
    if (index != -1) {
      tabs.remove(index);
      panel.remove(index);

      // Update selected tab index if necessary.
      if (selectionIndex == index) {
        selectionIndex = -1;
      }
    }
  }

  /**
   * Selects a tab, if this tab menu contains it.
   *
   * @param index the index of the tab to select
   */
  public void selectTab(int index) {
    // Only select tab if it is present.
    if (index < 0 || index >= tabs.size()) {
      return;
    }

    // Deselect current tab.
    if (selectionIndex != -1) {
      Widget widget = panel.getWidget(selectionIndex);
      styleWidget(widget, false);
    }

    // Select new tab.
    selectionIndex = index;
    styleWidget(panel.getWidget(selectionIndex), true);
  }

  /**
   * Applies styling to a tab widget. See the class javadoc to determine which
   * styles are applied.
   *
   * @param widget the tab widget to style
   * @param selected true if the tab is selected
   */
  private void styleWidget(Widget widget, boolean selected) {
    // Figure out the style to apply.
    String style = isFirstWidget(widget) ? FIRST_TAB_STYLE : TAB_STYLE;
    style += selected ? SELECTED_TAB_STYLE_SUFFIX : "";

    // Apply the style.
    widget.setStyleName(style);
    String color = selected ? tabs.get(selectionIndex).getBackgroundColor() :"";
    setStyleAttribute(widget.getElement(), BACKGROUND_COLOR, color);
  }

  /**
   * Returns true if a widget is the first widget of this menu.
   *
   * @param widget the widget to verify
   * @return true if it's the first widget
   */
  private boolean isFirstWidget(Widget widget) {
    return panel.getWidget(0).equals(widget);
  }
}
